#include "wl_core.h"
#include "wl_malloc.h"
#include "wl_message_queue.h"
#include "wl_timer_inner.h"
#include "wl_trace.h"
#include "wl_utils.h"

#include "wl_ext_os.h"
#include "wl_ext_network.h"
#include "wl_ext_player.h"
#include "wl_ext_recorder.h"
#include "wl_net.h"

typedef wl_ext_task WL_TASK;
#define WL_INVALID_TASK 0

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

const wl_int32 sTaskPrority[WL_TASK_MAX] = {EXT_TASK_PRIORITY_NORMAL, EXT_TASK_PRIORITY_NORMAL, EXT_TASK_PRIORITY_LOW};
const wl_int32 sTaskStack[WL_TASK_MAX]   = {16 * 1024, 8 * 1024, 8 * 1024};

#define WL_TASK_NAME_LEN 20

typedef struct {
    wl_bool                  initialized;
    wl_bool                  isstart;
    wl_bool                  exit;
    WL_TASK                  task;
    WL_MESSAGE_QUEUE         queue;
    wl_char                  name[WL_TASK_NAME_LEN];
    wl_task_message_callback callback;
} wl_task_info;

typedef struct {
    wl_bool      initialized;
    wl_ext_mutex mutex;
    wl_module    modules[WL_MODULE_MAX];
    wl_task_info tasks[WL_TASK_MAX];
} wl_core_global;

static wl_core_global s_core_global;

#define CHECK_MODULE(moduleid) (0 > moduleid || moduleid > WL_MODULE_MAX) ? wl_false : wl_true
#define CHECK_TASK(taskid) (0 > taskid || taskid > WL_TASK_MAX) ? wl_false : wl_true


void task_entry(wl_ext_task task, void *pParam) {
    wl_int32 taskid = (wl_int32)pParam;
    wl_task_message          task_message;
    wl_task_message_callback callback;

    s_core_global.tasks[taskid].isstart = wl_true;
    WLLOGW("taskid %d entry", taskid);
    while (!s_core_global.tasks[taskid].exit) {
        memset(&task_message, 0, sizeof(wl_task_message));
        // WLLOGD("wl_message_queue_receive task %d", taskid);
        if (0
            == wl_message_queue_receive(
                &s_core_global.tasks[taskid].queue, (void*)&task_message, sizeof(wl_task_message))) {
            wl_ext_mutex_lock(s_core_global.mutex, WL_EXT_WAIT_FOREVER);
            callback = s_core_global.tasks[taskid].callback;
            wl_ext_mutex_unlock(s_core_global.mutex);
            if (wl_null != callback) {
                callback(&task_message);
                // WLLOGT("task %d callback", taskid);
            } else {
                // WLLOGW("task %d callback is null", taskid);
            }
        } else {
            wl_msleep(500);
            WLLOGW("wl_message_queue_receive task %d fail", taskid);
        }
    }

    WLLOGT("taskid %d exit", taskid);
}

static wl_int32
    task_create(wl_int32 taskid, wl_int32 queue_num, wl_int32 queue_size, wl_task_message_callback callback) {
    if (!CHECK_TASK(taskid)) {
        WLLOGW("invalid task %d", taskid);
        return -1;
    }

    if (!s_core_global.tasks[taskid].initialized) {
        int stacksize = 0;
        int priority  = 0;
        int ret = 0;

        s_core_global.tasks[taskid].isstart = wl_false;
        sprintf(s_core_global.tasks[taskid].name, "wl_t_%d", taskid);
        sprintf(s_core_global.tasks[taskid].queue.name, "wl_q_%d", taskid);

        WLLOGW("wl_message_queue_create task %d queue %d", taskid, queue_num);
        if (-1
            == wl_message_queue_create(
                s_core_global.tasks[taskid].queue.name, &s_core_global.tasks[taskid].queue, queue_num, queue_size)) {
            WLLOGW("wl_message_queue_create task %d fail", taskid);
            return -1;
        }
        WLLOGT("wl_message_queue_create task %d queue %d",
               s_core_global.tasks[taskid].name,
               s_core_global.tasks[taskid].queue.name);

        stacksize = sTaskStack[taskid];
        priority = sTaskPrority[taskid];
        WLLOGW("wl_ext_task_create task %d stacksize %d priority %d", taskid, stacksize, priority);
        ret = wl_ext_task_static_create(&s_core_global.tasks[taskid].task, s_core_global.tasks[taskid].name, task_entry, (void*)taskid);
        //ret = wl_ext_task_create(&s_core_global.tasks[taskid].task, s_core_global.tasks[taskid].name, stacksize, priority, 32, task_entry, (void*)taskid);
        if (ret != 0) {
            WLLOGW("ql_rtos_task_create task %d fail", taskid);
            wl_message_queue_delete(&s_core_global.tasks[taskid].queue);
            return -1;
        }
        s_core_global.tasks[taskid].initialized = wl_true;
        s_core_global.tasks[taskid].exit        = wl_false;
        s_core_global.tasks[taskid].callback    = callback;
    }
    return 0;
}

static wl_int32 task_delete(wl_int32 taskid) {
    if (!CHECK_TASK(taskid)) {
        WLLOGW("invalid task %d", taskid);
        return -1;
    }
    if (s_core_global.tasks[taskid].initialized) {
        s_core_global.tasks[taskid].isstart = wl_false;
        s_core_global.tasks[taskid].exit    = wl_true;
        if (0 != wl_message_queue_delete(&s_core_global.tasks[taskid].queue)) {
            WLLOGW("wl_message_queue_delete fail");
        }
        // wl_ext_task_join(s_core_global.tasks[taskid].task);
        // wl_ext_task_destroy(s_core_global.tasks[taskid].task);
        wl_ext_task_static_destroy(s_core_global.tasks[taskid].task);

        WLLOGT("pthread_join task %d", taskid);
        s_core_global.tasks[taskid].initialized = wl_false;
        s_core_global.tasks[taskid].task        = WL_INVALID_TASK;
        s_core_global.tasks[taskid].callback    = wl_null;
    }
    return 0;
}

static wl_int32 process_module_message(wl_task_message* message) {
    wl_module_message module_message;
    if (wl_null == message || sizeof(wl_module_message) != message->datalen) {
        WLLOGW("message is null");
        return -1;
    }

    memcpy(&module_message, &(message->data), message->datalen);
    if (CHECK_MODULE(module_message.moduleid)) {
        wl_module* module = &s_core_global.modules[module_message.moduleid];
        if (module->callback) {
            // WLLOGT("module %d msg %d", module_message.moduleid, module_message.msgid);
            module->callback(module_message.msgid, module_message.data, module_message.datalen);
        } else {
            WLLOGW("module: %d callback is null", module_message.moduleid);
        }
    } else {
        WLLOGW("invalid module %d", module_message.moduleid);
    }
    if (wl_null != module_message.data) {
        wl_free(module_message.data);
    }

    return 0;
}

wl_int32 task_message_callback(wl_task_message* message) {
    if (wl_null == message) {
        WLLOGW("message is null");
        return -1;
    }
    // WLLOGT("msg type %d datalen %d", message->type, message->datalen);
    switch (message->type) {
        case WL_TASK_MSG_NET: {
            return wl_ext_net_process_message(message);
        }
        break;
        case WL_TASK_MSG_HTTP: {
            return wl_http_process_message(message);
        }
        break;
        case WL_TASK_MSG_TIMER:
            return wl_timer_process_message(message);
        case WL_TASK_MSG_DNS: {
            extern wl_int32 wl_dns_process_message(wl_task_message* message);
            return wl_dns_process_message(message);
        }
        case WL_TASK_MSG_MODULE:
            return process_module_message(message);
        default:
            WLLOGW("unknown msg type %d", message->type);
            break;
    }

    return -1;
}

wl_int32 wl_send_task_message(wl_int32 taskid, wl_task_message* message) {
    if (!CHECK_TASK(taskid)) {
        WLLOGW("invalid task %d", taskid);
        return -1;
    }
    if (!s_core_global.tasks[taskid].initialized) {
        WLLOGW("task %d not init", taskid);
        return -1;
    }
    if (!s_core_global.tasks[taskid].isstart) {
        WLLOGW("task %d not start", taskid);
        return -1;
    }

    if (0 != wl_message_queue_send(&s_core_global.tasks[taskid].queue, message, sizeof(wl_task_message))) {
        WLLOGT("wl_message_queue_send task %d fail", taskid);
        return -1;
    }
    // WLLOGD("wl_message_queue_send task %d", taskid);
    return 0;
}

wl_int32 wl_core_init(void) {
    WLLOGI("wl_core_init");
    //初始化socket timer task
    if (!s_core_global.initialized) {
        if (!wl_mem_init()) {
            WLLOGW("wl_mem_init fail");
            return -1;
        }

        if (0 != wl_ext_mutex_create(&s_core_global.mutex, "core")) {
            WLLOGW("wl_mutex_create fail");
            wl_mem_end();
            return -1;
        }
        if (0 != wl_ext_network_init()) {
            WLLOGW("wl_transport_init fail");
            wl_ext_mutex_destroy(s_core_global.mutex);
            wl_mem_end();
            return -1;
        }
        if (0 != wl_timer_init()) {
            WLLOGW("wl_timer_init fail");
            wl_ext_network_deinit();
            wl_ext_mutex_destroy(s_core_global.mutex);
            wl_mem_end();
            return -1;
        }
        {
            wl_int32 i = 0;
            for (i = 0; i < WL_TASK_MAX; i++) {
                if (0 != task_create(i, WL_MQ_MSG_NUM, sizeof(wl_task_message), task_message_callback)) {
                    wl_int32 index = 0;
                    WLLOGW("task_create task %d fail", i);
                    for (; index < i; index++) {
                        task_delete(index);
                    }

                    wl_timer_end();
                    wl_ext_network_deinit();
                    wl_ext_mutex_destroy(s_core_global.mutex);
                    wl_mem_end();
                    return -1;
                }
            }
        }
        s_core_global.initialized = wl_true;
    }
    WLLOGI("wl_core_init success");
    return 0;
}

void wl_core_end(void) {
    if (s_core_global.initialized) {
        wl_int32 index = 0;

        wl_ext_mutex_lock(s_core_global.mutex, WL_EXT_WAIT_FOREVER);
        wl_ext_network_deinit();
        wl_timer_end();
        for (; index < WL_TASK_MAX; index++) {
            task_delete(index);
        }
        wl_mem_end();
    }
    wl_ext_mutex_unlock(s_core_global.mutex);
    wl_ext_mutex_destroy(s_core_global.mutex);
    s_core_global.initialized = wl_false;
}

wl_int32 wl_core_send_message(wl_int32 moduleid, wl_int32 msgid, void* data, wl_int32 datalen) {
    wl_task_message   task_message;
    wl_module_message module_message;
    if (!CHECK_MODULE(moduleid)) {
        WLLOGW("invalid module %d", moduleid);
        return -1;
    }
    memset(&module_message, 0, sizeof(wl_module_message));
    module_message.moduleid = moduleid;
    module_message.msgid    = msgid;
    module_message.datalen  = datalen;
    if (wl_null != data && 0 < datalen) {
        module_message.data = wl_malloc(datalen);
        if (wl_null == module_message.data) {
            WLLOGW("malloc fail");
            return -1;
        }
        memcpy(module_message.data, data, datalen);
    }
    task_message.type    = WL_TASK_MSG_MODULE;
    task_message.datalen = sizeof(wl_module_message);
    memcpy(&task_message.data, &module_message, sizeof(wl_module_message));
    if (0 != wl_send_task_message(s_core_global.modules[moduleid].taskid, &task_message)) {
        if (wl_null != module_message.data) {
            wl_free(module_message.data);
            WLLOGT("wl_send_task_message task %d module %d msg %d fail",
                   s_core_global.modules[moduleid].taskid,
                   moduleid,
                   msgid);
            return -1;
        }
    }

    return 0;
}

wl_int32 wl_core_register_module(wl_module* module) {
    if (wl_null == module) {
        WLLOGW("module is null");
        return -1;
    }
    if (!CHECK_MODULE(module->moduleid)) {
        WLLOGW("invalid module %d", module->moduleid);
        return -1;
    }
    if (!s_core_global.initialized) {
        WLLOGW("core not init");
        return -1;
    }
    wl_ext_mutex_lock(s_core_global.mutex, WL_EXT_WAIT_FOREVER);
    memcpy(&s_core_global.modules[module->moduleid], module, sizeof(wl_module));
    wl_ext_mutex_unlock(s_core_global.mutex);
    return 0;
}

void wl_core_unregister_module(wl_int32 moduleid) {
    if (!s_core_global.initialized) {
        WLLOGW("core not init");
        return;
    }
    if (!CHECK_MODULE(moduleid)) {
        WLLOGW("invalid module %d", moduleid);
        return;
    }
    wl_ext_mutex_lock(s_core_global.mutex, WL_EXT_WAIT_FOREVER);
    memset(&s_core_global.modules[moduleid], 0, sizeof(wl_module));
    wl_ext_mutex_unlock(s_core_global.mutex);
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
